//////////////////////////////////////////////////////////////////////////
//
// This file is a part of XORT project.
//
// PROJECT:         Xort compiler execution subsystem
// FILE:            xgen.cpp - all stuff related object code generation and jit
// AUTHOR:          Vladimir Gumenuk
// DATE:            7-Oct-2014
// MODIFICATIONS:
//
//
//     Copyright(c) 2014, Vladimir Gumenuk
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification, 
//     are permitted provided that the following conditions are met :
//
//     1. Redistributions of source code must retain the above copyright notice, 
//        this list of conditions and the following disclaimer.
//     2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
//        the following disclaimer in the documentation and / or other materials provided with the distribution.
//     3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 
//        or promote products derived from this software without specific prior written permission.
//
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
//     THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//     OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
//     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "xgen.h"

XGenerator::XGenerator( void *pMemory, size_t dwSize ) : CodeGenerator( dwSize, pMemory )
{

}

XGenerator::~XGenerator()
{

}

Xbyak::Reg XGenerator::findRegFromIdx( Register64 rg, int iSize /*= 64*/ )
{
    if( iSize == 64 )
    {
        switch( rg )
        {
        case RAX:  return rax;
        case RCX:  return rcx;
        case RDX:  return rdx;
        case RBX:  return rbx;
        case RSP:  return rsp;
        case RBP:  return rbp;
        case RSI:  return rsi;
        case RDI:  return rdi;
        case R8:   return r8;
        case R9:   return r9;
        case R10:  return r10;
        case R11:  return r11;
        case R12:  return r12;
        case R13:  return r13;
        case R14:  return r14;
        case R15:  return r15;
        }
    }
    else if( iSize == 32 )
    {
        switch ( rg )
        {
        case RAX:  return eax;
        case RCX:  return ecx;
        case RDX:  return edx;
        case RBX:  return ebx;
        case RSP:  return esp;
        case RBP:  return ebp;
        case RSI:  return esi;
        case RDI:  return edi;
        case R8:   return r8d;
        case R9:   return r9d;
        case R10:  return r10d;
        case R11:  return r11d;
        case R12:  return r12d;
        case R13:  return r13d;
        case R14:  return r14d;
        case R15:  return r15d;
        }
    }
    else if( iSize == 16 )
    {
        switch ( rg )
        {
        case RAX:  return ax;
        case RCX:  return cx;
        case RDX:  return dx;
        case RBX:  return bx;
        case RSP:  return sp;
        case RBP:  return bp;
        case RSI:  return si;
        case RDI:  return di;
        case R8:   return r8w;
        case R9:   return r9w;
        case R10:  return r10w;
        case R11:  return r11w;
        case R12:  return r12w;
        case R13:  return r13w;
        case R14:  return r14w;
        case R15:  return r15w;
        }
    }
    else if( iSize == 8 )
    {
        switch ( rg )
        {
        case RAX:  return al;
        case RCX:  return cl;
        case RDX:  return dl;
        case RBX:  return bl;
        case RSP:  return al;
        case RBP:  return al;
        case RSI:  return al;
        case RDI:  return al;
        case R8:   return r8b;
        case R9:   return r9b;
        case R10:  return r10b;
        case R11:  return r11b;
        case R12:  return r12b;
        case R13:  return r13b;
        case R14:  return r14b;
        case R15:  return r15b;
        }
    }
    return rax;
}

//////////////////////////////////////////////////////////////////////////
//
void XGenerator::push_GPR( Register64 rg )
{
    push( findRegFromIdx( rg ) );
}

void XGenerator::pop_GPR( Register64 rg )
{
    pop( findRegFromIdx( rg ) );
}

//////////////////////////////////////////////////////////////////////////
//
void XGenerator::mov_GPR_GPR( Register64 regDst, Register64 regSrc )
{
    mov( findRegFromIdx( regDst ), findRegFromIdx( regSrc ) );
}

//////////////////////////////////////////////////////////////////////////
//
void XGenerator::ret_Drop( int iNumBytes )
{
    ret( iNumBytes );
}

//////////////////////////////////////////////////////////////////////////
//
void XGenerator::push_imm_byte( BYTE val )
{
    push( byte, val );
}
void XGenerator::push_imm_word( WORD val )
{
    push( word, val );
}
void XGenerator::push_imm_dword( DWORD val )
{
    push( dword, val );
}
void XGenerator::push_imm_qword( QWORD val )
{
    mov( rax, val );
    push( rax );
}

//////////////////////////////////////////////////////////////////////////
//
void XGenerator::mov_GPR_imm_byte( Register64 regDst, BYTE val ) // mov rax, FF
{
    mov( findRegFromIdx( regDst ), val );
}
void XGenerator::mov_GPR_imm_word( Register64 regDst, WORD val ) // mov rax, FFFF
{
    mov( findRegFromIdx( regDst ), val );
}
void XGenerator::mov_GPR_imm_dword( Register64 regDst, DWORD val ) // mov rax, FFFFFFFF
{
    mov( findRegFromIdx( regDst ), val );
}
void XGenerator::mov_GPR_imm_qword( Register64 regDst, QWORD val )
{
    mov( findRegFromIdx( regDst ), val );
}

//////////////////////////////////////////////////////////////////////////
//
void XGenerator::mov_byte_ptr_Base_disp_GPR( Register64 regBase, DWORD dwDisp, Register64 regMov ) // mov byte ptr [rbp+8], rax
{
    mov( byte[ findRegFromIdx( regBase ) + dwDisp ], findRegFromIdx( regMov, 8 ) );
}
void XGenerator::mov_word_ptr_Base_disp_GPR( Register64 regBase, DWORD dwDisp, Register64 regMov ) // mov word ptr [rbp+8], ax
{
    mov( word[findRegFromIdx( regBase ) + dwDisp], findRegFromIdx( regMov, 16 ) );
}
void XGenerator::mov_dword_ptr_Base_disp_GPR( Register64 regBase, DWORD dwDisp, Register64 regMov ) // mov dword ptr [rbp+8], eax
{
    mov( dword[findRegFromIdx( regBase ) + dwDisp], findRegFromIdx( regMov, 32 ) );
}
void XGenerator::mov_qword_ptr_Base_disp_GPR( Register64 regBase, DWORD dwDisp, Register64 regMov ) // mov qword ptr [rbp+8], rax
{
    mov( qword[findRegFromIdx( regBase ) + dwDisp], findRegFromIdx( regMov, 64 ) );
}

//////////////////////////////////////////////////////////////////////////
//
void XGenerator::mov_byte_ptr_MEM_GPR( QWORD qwAddrDst, Register64 rg )        // mov byte ptr Addr, rax
{
    mov( byte [qwAddrDst], findRegFromIdx( rg, 8 ) );
}
void XGenerator::mov_word_ptr_MEM_GPR( QWORD qwAddrDst, Register64 rg )        // mov word ptr Addr, rax
{
    mov( word [qwAddrDst], findRegFromIdx( rg, 16 ) );
}
void XGenerator::mov_dword_ptr_MEM_GPR( QWORD qwAddrDst, Register64 rg )       // mov dword ptr Addr, rax
{
    mov( dword[ qwAddrDst ], findRegFromIdx( rg, 32 ) );
}
void XGenerator::mov_qword_ptr_MEM_GPR( QWORD qwAddrDst, Register64 rg )       // mov qword ptr Addr, rax
{
    mov( ptr[ qwAddrDst ], findRegFromIdx( rg, 64 ) );
}

//////////////////////////////////////////////////////////////////////////
//
void XGenerator::mov_byte_ptr_MEM_Base_disp_GPR( QWORD qwAddrDst, DWORD dwDisp, Register64 rg ) // mov byte ptr Addr, [rg+disp]
{
    mov( byte [qwAddrDst], byte [ findRegFromIdx( rg, 8 ) + dwDisp ] );
}
void XGenerator::mov_word_ptr_MEM_Base_disp_GPR( QWORD qwAddrDst, DWORD dwDisp, Register64 rg ) // mov word ptr Addr, [rg+disp]
{
    mov( word [qwAddrDst], word [ findRegFromIdx( rg, 16 ) + dwDisp ] );
}
void XGenerator::mov_dword_ptr_MEM_Base_disp_GPR( QWORD qwAddrDst, DWORD dwDisp, Register64 rg ) // mov dword ptr Addr, [rg+disp]
{
    mov( dword [qwAddrDst], dword [ findRegFromIdx( rg, 32 ) + dwDisp ] );
}
void XGenerator::mov_qword_ptr_MEM_Base_disp_GPR( QWORD qwAddrDst, DWORD dwDisp, Register64 rg ) // mov qword ptr Addr, [rg+disp]
{
    mov( qword [qwAddrDst], qword [ findRegFromIdx( rg ) + dwDisp ] );
}

//////////////////////////////////////////////////////////////////////////
//
void XGenerator::mov_GPR_byte_ptr_Base_disp(  Register64 rgDst, Register64 rgBase, DWORD dwDisp ) // mov rgDst, byte ptr [rgSrc+disp]
{
    mov( findRegFromIdx( rgDst, 8 ), byte [ findRegFromIdx( rgBase, 64 ) + dwDisp ] );
}
void XGenerator::mov_GPR_word_ptr_Base_disp(  Register64 rgDst, Register64 rgBase, DWORD dwDisp ) // mov rgDst, word ptr [rgSrc+disp]
{
    mov( findRegFromIdx( rgDst, 16 ), word [ findRegFromIdx( rgBase, 64 ) + dwDisp ] );
}
void XGenerator::mov_GPR_dword_ptr_Base_disp( Register64 rgDst, Register64 rgBase, DWORD dwDisp ) // mov rgDst, dword ptr [rgSrc+disp]
{
    mov( findRegFromIdx( rgDst, 32 ), dword [ findRegFromIdx( rgBase, 64 ) + dwDisp ] );
}
void XGenerator::mov_GPR_qword_ptr_Base_disp( Register64 rgDst, Register64 rgBase, DWORD dwDisp ) // mov rgDst, qword ptr [rgSrc+disp]
{
    mov( findRegFromIdx( rgDst, 64 ), qword [ findRegFromIdx( rgBase, 64 ) + dwDisp ] );
}

//////////////////////////////////////////////////////////////////////////
//
void XGenerator::deref_GPR_byte_ptr( Register64 rg )  // mov rax, byte  ptr[rax]
{
    mov( findRegFromIdx( rg, 8 ), byte[findRegFromIdx( rg )] );
}
void XGenerator::deref_GPR_word_ptr( Register64 rg )  // mov rax, word  ptr[rax]
{
    mov( findRegFromIdx( rg, 16 ), word[findRegFromIdx( rg )] );
}
void XGenerator::deref_GPR_dword_ptr( Register64 rg ) // mov rax, dword ptr[rax]
{
    mov( findRegFromIdx( rg, 32 ), dword[findRegFromIdx( rg )] );
}
void XGenerator::deref_GPR_qword_ptr( Register64 rg )
{
    mov( findRegFromIdx( rg, 64 ), ptr[ findRegFromIdx( rg ) ] ); // mov rax, qword ptr[rax]
}
void XGenerator::deref_GPR_qword_ptr_GPR( Register64 regDst, Register64 regSrc )         // mov rax, qword ptr[rdx]
{
    mov( findRegFromIdx( regDst ), ptr[ findRegFromIdx( regSrc ) ] ); // mov rax, qword ptr[rdx]
}

//////////////////////////////////////////////////////////////////////////
//
void XGenerator::sub_GPR_imm_byte( Register64 rg, BYTE val )  // sub rax, 0xFF
{
    sub( findRegFromIdx( rg ), val );
}

void XGenerator::add_GPR_imm_byte( Register64 rg, BYTE val )  // add rax, 0xFF
{
    add( findRegFromIdx( rg ), val );
}

//////////////////////////////////////////////////////////////////////////
//
void XGenerator::declare_label( int iIdx )
{
    char szBuf[64];
    sprintf( szBuf, "L%d", iIdx );

    L( szBuf );
}
void XGenerator::goto_label( int iIdx )
{
    char szBuf[64];
    sprintf( szBuf, "L%d", iIdx );

    jmp( szBuf );
}

//////////////////////////////////////////////////////////////////////////
//
void XGenerator::cmp_byte_GPR_GPR(  Register64 rg1, Register64 rg2 )   // cmp al, bl
{
    cmp( findRegFromIdx( rg1, 8 ), findRegFromIdx( rg2, 8 ) );
}
void XGenerator::cmp_word_GPR_GPR(  Register64 rg1, Register64 rg2 )   // cmp ax, bx
{
    cmp( findRegFromIdx( rg1, 16 ), findRegFromIdx( rg2, 16 ) );
}
void XGenerator::cmp_dword_GPR_GPR( Register64 rg1, Register64 rg2 )   // cmp eax, ebx
{
    cmp( findRegFromIdx( rg1, 32 ), findRegFromIdx( rg2, 32 ) );
}
void XGenerator::cmp_qword_GPR_GPR( Register64 rg1, Register64 rg2 )   // cmp rax, rbx
{
    cmp( findRegFromIdx( rg1, 64 ), findRegFromIdx( rg2, 64 ) );
}

//////////////////////////////////////////////////////////////////////////
//
void XGenerator::cmp_GPR_byte_ptr_Base_disp(  Register64 rg, Register64 rgBase, DWORD dwDisp )   // cmp al, byte ptr[rg+disp]
{
    cmp( findRegFromIdx( rg, 8 ), byte[ findRegFromIdx( rgBase, 64 ) + dwDisp ] );
}
void XGenerator::cmp_GPR_word_ptr_Base_disp(  Register64 rg, Register64 rgBase, DWORD dwDisp )   // cmp ax, word ptr[rg+disp]
{
    cmp( findRegFromIdx( rg, 16 ), byte[ findRegFromIdx( rgBase, 64 ) + dwDisp ] );
}
void XGenerator::cmp_GPR_dword_ptr_Base_disp( Register64 rg, Register64 rgBase, DWORD dwDisp )   // cmp eax, dword ptr[rg+disp]
{
    cmp( findRegFromIdx( rg, 32 ), byte[ findRegFromIdx( rgBase, 64 ) + dwDisp ] );
}
void XGenerator::cmp_GPR_qword_ptr_Base_disp( Register64 rg, Register64 rgBase, DWORD dwDisp )   // cmp rax, qword ptr[rg+disp]
{
    cmp( findRegFromIdx( rg, 64 ), byte[ findRegFromIdx( rgBase, 64 ) + dwDisp ] );
}

//////////////////////////////////////////////////////////////////////////
//
void XGenerator::cmp_byte_ptr_Base_disp_GPR(  Register64 rgBase, DWORD dwDisp, Register64 rg )   // cmp byte ptr[rg+disp], al
{
    cmp( byte[ findRegFromIdx( rgBase, 64 ) + dwDisp ], findRegFromIdx( rg, 8 ) );
}
void XGenerator::cmp_word_ptr_Base_disp_GPR(  Register64 rgBase, DWORD dwDisp, Register64 rg )   // cmp word ptr[rg+disp], ax
{
    cmp( byte[ findRegFromIdx( rgBase, 64 ) + dwDisp ], findRegFromIdx( rg, 16 ) );
}
void XGenerator::cmp_dword_ptr_Base_disp_GPR( Register64 rgBase, DWORD dwDisp, Register64 rg )   // cmp dword ptr[rg+disp], eax
{
    cmp( byte[ findRegFromIdx( rgBase, 64 ) + dwDisp ], findRegFromIdx( rg, 32 ) );
}
void XGenerator::cmp_qword_ptr_Base_disp_GPR( Register64 rgBase, DWORD dwDisp, Register64 rg )   // cmp qword ptr[rg+disp], rax
{
    cmp( byte[ findRegFromIdx( rgBase, 64 ) + dwDisp ], findRegFromIdx( rg, 64 ) );
}
